package com.gitee.wsl.concurrent

import com.gitee.wsl.platform.concurrent.runBlocking
import com.gitee.wsl.concurrent.executor.Executor
import com.gitee.wsl.platform.thread.Thread
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.yield

/**
 * A callback function that is invoked when a future completes.
 *
 * @param T The type of the value that the future will complete with.
 */
typealias CompletionCallback<T> = (T) -> Unit

/**
 * Represents a value that may be available in the future.
 * This interface provides methods to check if the value is available and to wait for it.
 *
 * @param T The type of the value that the future will complete with.
 */
interface Future<T> {


    /**
     * The current value of the future, or null if the future has not completed yet.
     */
    val value: T?

    /**
     * Indicates whether the future has completed.
     * When this is true, [value] will contain the result.
     */
    val isCompleted: Boolean

    /**
     * Blocks the current thread until the future completes and returns the result.
     * This method will yield the current thread's execution time to other threads
     * while waiting for the result.
     *
     * @return The value that the future completed with.
     * @throws IllegalStateException If the future completed but the value is null.
     */
    fun await(): T {
        while (!isCompleted) Thread.yield()
        return requireNotNull(value) { "Could not await result of CompletableFuture" }
    }

    val isCancelled: Boolean get() = false
    fun cancel(bool: Boolean) {}
}


/**
 * Converts a [Deferred] to a [Future].
 * This allows using a coroutine's [Deferred] result with Filament's [Future] API.
 *
 * @param T The type of the value that the future will complete with.
 * @return A [Future] that completes when the [Deferred] completes.
 */
@OptIn(ExperimentalCoroutinesApi::class)
fun <T> Deferred<T>.asFuture(): Future<T> = object : Future<T> {
    override val value: T
        get() = getCompleted()

    override val isCompleted: Boolean
        get() = this@asFuture.isCompleted

    override fun cancel(bool: Boolean) {
        this@asFuture.cancel()
    }

    override val isCancelled: Boolean
        get() = this@asFuture.isCancelled

    override fun await(): T = runBlocking {
        this@asFuture.await()
    }
}

/**
 * Creates a [CompletableFuture] that will be completed with the result of the provided suspending block.
 * The block will be executed asynchronously on the provided executor.
 * This is similar to [CompletableFuture.Companion.async], but supports suspending functions.
 *
 * @param T The type of the value that the future will complete with.
 * @param executor The executor to run the block on.
 * @param block The suspending function to execute asynchronously.
 * @return A [CompletableFuture] that will be completed with the result of the block.
 */
inline fun <T> CompletableFuture.Companion.asyncSuspend( // @formatter:off
    executor: Executor,
    crossinline block: suspend () -> T
): CompletableFuture<T> { // @formatter:on
    val future = CompletableFuture<T>()
    executor.exec {
        runBlocking { future.complete(block()) }
    }
    return future
}

/**
 * Suspends the current coroutine until the future completes and returns the result.
 * This method will yield the current coroutine's execution time to other coroutines
 * while waiting for the result, rather than blocking the thread like [Future.await].
 *
 * @param T The type of the value that the future will complete with.
 * @return The value that the future completed with.
 * @throws IllegalArgumentException If the future completed but the value is null.
 */
suspend fun <T> Future<T>.awaitSuspend(): T {
    while(!isCompleted) yield()
    return requireNotNull(value) { "Could not await result of Future" }
}
